#include <stdio.h>
#include <string.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/reboot.h>
#include <zephyr/logging/log.h>

#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/hci.h>

#include <hal/nrf_power.h>

#include "configs.h"
#include "ble_transport_svc.h"
// #include "common.h"
// #include "ble_pkt.h"
// #include "ser_codec.h"

LOG_MODULE_REGISTER(ble, LOG_LEVEL_DBG);

#define DEVICE_NAME CONFIG_BT_DEVICE_NAME
#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)

static void scan_result(const struct bt_le_scan_recv_info *info, struct net_buf_simple *buf);
static void scan_timeout(void);
static void start_scan(void);

static const struct bt_data ad[] = {
    BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
    BT_DATA_BYTES(BT_DATA_MANUFACTURER_DATA,
                  SW_VERSION_MAJOR, SW_VERSION_MINOR, SW_VERSION_REVISION, SW_VERSION_BUILD),
};

static const struct bt_data sd[] = {
    BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
};

static struct bt_le_scan_cb scan_cb = {
    .recv = scan_result,
    .timeout = scan_timeout,
};

/**
 * CMD: USBDFU
 *  - 0x55, 0x53, 0x42, 0x44, 0x46, 0x55
 */
static void on_received(struct bt_conn *conn, const uint8_t *const data, uint16_t len)
{
    ARG_UNUSED(conn);
    LOG_HEXDUMP_DBG(data, len, "ble received: ");

    if (memcmp((char *)"USBDFU", (char *)data, 6) == 0)
    {
        uint32_t reas;
        static uint8_t zephyr_reset_type = 0xFF;
        zephyr_reset_type = nrf_power_gpregret_get(NRF_POWER, 0);

        LOG_DBG("zephyr_reset_type: 0x%X\r\n", zephyr_reset_type);

        reas = nrf_power_resetreas_get(NRF_POWER);
        LOG_DBG("reset reason: 0x%X\r\n", reas);

        nrf_power_gpregret_set(NRF_POWER, 0, 0xB1);

        // sys_reboot(SYS_REBOOT_COLD);
        NVIC_SystemReset();
    }
}

void on_ccc_changed(uint16_t value)
{
    LOG_DBG("value: %d\r\n", value);
}

static struct ble_transport_cb ble_transport_callbacks = {
    .ccc_changed = on_ccc_changed,
    .received = on_received,
};

static void scan_result(const struct bt_le_scan_recv_info *info, struct net_buf_simple *buf)
{
    char dev[BT_ADDR_LE_STR_LEN];

    // uint8_t adv_pkt[45] = {0};
    // uint8_t adv_pkt_len = 0;

    // struct ble_pkt_scan_result *result = (struct ble_pkt_scan_result *)adv_pkt;
    // for (int i = 0; i < 6; i++)
    // {
    //     result->mac[i] = info->addr->a.val[5 - i];
    // }
    // result->rssi = info->rssi;
    // result->adv_type = info->adv_type;
    // result->adv_data_len = buf->len;
    // if (buf->len > 0)
    // {
    //     memcpy(&adv_pkt[sizeof(struct ble_pkt_scan_result)], buf->data, buf->len);
    // }

    // adv_pkt_len = sizeof(struct ble_pkt_scan_result) + buf->len;

    // ser_encode(adv_pkt, adv_pkt_len);

    // debug info
    bt_addr_le_to_str(info->addr, dev, sizeof(dev));
    LOG_INF("[DEVICE]: %s, AD evt type %u, AD data len %u, RSSI %i\n",
            dev, info->adv_type, buf->len, info->rssi);
}

static void scan_timeout(void)
{
    LOG_INF("scan timeout.\n");
}

static void start_scan(void)
{
    int err;
    struct bt_le_scan_param scan_param = {
        .type = BT_LE_SCAN_TYPE_ACTIVE,
        .options = BT_LE_SCAN_OPT_NONE,
        .interval = BT_GAP_ADV_FAST_INT_MAX_2,
        .window = BT_GAP_ADV_FAST_INT_MIN_2,
    };

    err = bt_le_scan_start(&scan_param, NULL);
    if (err)
    {
        LOG_ERR("%s: Scanning failed to start (err %d)\n", __func__, err);
        return;
    }

    LOG_INF("%s: Scanning successfully started\n", __func__);
}

static void adv_start(void)
{
    struct bt_le_adv_param *adv_param =
        BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE |
                            BT_LE_ADV_OPT_ONE_TIME,
                        BT_GAP_ADV_FAST_INT_MIN_2,
                        BT_GAP_ADV_FAST_INT_MAX_2,
                        NULL);
    int err;

    err = bt_le_adv_start(adv_param, ad, ARRAY_SIZE(ad), sd,
                          ARRAY_SIZE(sd));
    if (err)
    {
        printk("Failed to start advertiser (%d)\n", err);
        return;
    }
}

static int bt_ble_init(void)
{
    int err;

    err = bt_enable(NULL);
    if (err)
    {
        LOG_ERR("Bluetooth init failed (err %d)\n", err);
        return -1;
    }
    LOG_INF("Bluetooth initialized\n");

    bt_le_scan_cb_register(&scan_cb);
    // bt_conn_cb_register(&conn_cb);
    // bt_gatt_cb_register(&gatt_cb);

    // start_scan();

    err = ble_transport_svc_init(&ble_transport_callbacks);
    if (err)
    {
        LOG_ERR("Failed to init BLE transport service (err %d)", err);
        return -1;
    }
    adv_start();

    return 0;
}

static int init_fn(void)
{
    return bt_ble_init();
}

SYS_INIT(init_fn, APPLICATION, 88);
